package com.swak.paxos;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import org.springframework.util.Assert;

import com.swak.paxos.statemachine.StateMachine;
import com.swak.reactivex.cluster.ClusterConfig;
import com.swak.utils.Lists;
import com.swak.utils.Maps;
import com.swak.utils.Sets;

import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;

@Getter
@Setter
@Accessors(chain = true)
public class PaxosConfig {

	/**
	 * 设置 NodeID
	 */
	private String nodeID;

	/**
	 * PaxosLog 存储路径
	 */
	private String paxosLogStoragePath;

	/**
	 * PaxosLog 文件大小 100M
	 */
	private int paxosLogFileSize = 1024 * 1024 * 100;

	/**
	 * PaxosLog 记录最大值 10k
	 */
	private int paxosLogRecordSize = 1024 * 10;

	/**
	 * 时间单位
	 */
	private TimeUnit timeUnit = TimeUnit.MILLISECONDS;

	/**
	 * 提交议案的超时时间: 提交到被集群接受的总时间
	 */
	private int commitTimeoutMs = 3000;

	/**
	 * 提交议案的超时时间: 提交到被集群接受的总时间
	 */
	private int maxCommitTimeoutMs = 20000;

	/**
	 * 最大的提升编号的次数，如果超过此次数，则需要等待一定的超时的时间
	 */
	private int maxProposeCount = 100;

	/**
	 * 第一阶段：提交的时间
	 */
	private int prepareTimeoutMs = 1000;

	/**
	 * 第一阶段：提交的时间 最大时间
	 */
	private long maxPrepareTimeoutMs = 8000;

	/**
	 * 第二阶段： 接受的时间
	 */
	private int acceptTimeoutMs = 1000;

	/**
	 * 第二阶段： 接受的时间 最大时间
	 */
	private long maxAcceptTimeoutMs = 8000;

	/**
	 * 学习的时间
	 */
	private long askForLearnTimeoutMs = 3000;

	/**
	 * Master 租期  30s
	 */
	private long masterLeaseTimeMS = 30000;

	// ############## 学习 #################

	private long learnerSenderPrepareTimeoutMs = 5000;

	// ############## 刷盘 #################

	/**
	 * PhysicLog刷盘间隔时间（定时任务的时间）
	 */
	private int flushPhysicLogIntervalMs = 1000;

	/**
	 * PhysicLog刷盘间隔时间（实际的刷盘间隔）
	 */
	private int flushPhysicLogThoroughIntervalMs = 1000 * 10;

	/**
	 * 至少刷几个PAGE了，即超过 4个 page（4K）才刷盘
	 */
	private int flushPhysicLogLeastPages = 4;

	/**
	 * 清理长期不使用的 mmaped file
	 */
	private int cleanPhyMappedFileIntervalMs = 10000;

	/**
	 * 集群配置
	 */
	private ClusterConfig cluster = new ClusterConfig();

	/**
	 * 状态机
	 */
	private StateMachineConfig smFac = new StateMachineConfig();

	/**
	 * 分组配置
	 */
	private List<GroupConfig> groups = Lists.newArrayList();

	/**
	 * 分组配置
	 * 
	 * @author DELL
	 */
	@Getter
	@Setter
	@Accessors(chain = true)
	public static class GroupConfig {

		/**
		 * 分组ID
		 */
		private int groupID;

		/**
		 * 是否是 Follower 节点, Follower 不能提交数据，不参与 Paxos 算法；可以学习其他节点的数据
		 */
		private boolean isIMFollower;

		/**
		 * 启动时设置的同步集合，默认自定添加当前的机器；
		 */
		private List<String> nodes = Lists.newArrayList();
	}

	/**
	 * 自定义的 - 状态机
	 */
	@Getter
	@Setter
	@Accessors(chain = true)
	public static class StateMachineConfig {

		Set<StateMachine> globalSM = Sets.newHashSet();
		Map<Integer, List<StateMachine>> groupSM = Maps.newHashMap();

		public StateMachineConfig addGroupSM(int groupID, StateMachine sm) {
			Assert.isTrue(sm.getSmID() > 0, "SmID Must Bigger Than ZERO.");
			List<StateMachine> sms = this.groupSM.computeIfAbsent(groupID, (key) -> {
				return Lists.newArrayList();
			});
			sms.add(sm);
			return this;
		}

		public StateMachineConfig addGlobalSM(StateMachine sm) {
			Assert.isTrue(sm.getSmID() > 0, "SmID Must Bigger Than ZERO.");
			this.globalSM.add(sm);
			return this;
		}

		public List<StateMachine> getSms(int groupID) {
			List<StateMachine> sms = Lists.newArrayList();
			sms.addAll(this.globalSM);
			sms.addAll(this.groupSM.getOrDefault(groupID, Lists.newArrayList()));
			return sms;
		}
	}
}
